package com.xpalive.base.valid.adapter;

import com.xpalive.base.valid.ComparableSeqValid;
import com.xpalive.base.valid.ComparableSeqValid.Sequence;
import com.xpalive.base.valid.ComparableSeqValid.Sequences;
import java.lang.reflect.Field;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class ComparableSeqValidator implements ConstraintValidator<ComparableSeqValid, Object> {

    @Override
    public void initialize(ComparableSeqValid constraintAnnotation) {
    }

    @Override
    public boolean isValid(Object object, ConstraintValidatorContext context) {
        Field[] declaredFields = object.getClass().getDeclaredFields();

        //注解字段分组
        Map<String, List<OrderAndCompareValue>> groupCompareMap = new HashMap<>();
        for (Field declaredField : declaredFields) {
            setValue2Map(groupCompareMap, declaredField, object);
        }

        List<ErrorSequenceInfo> errorSequenceInfoList = groupCompareMap.entrySet().parallelStream()
            .flatMap(entry -> {
                //按照Index排序
                String key = entry.getKey();
                List<OrderAndCompareValue> orderAndCompareValueList = entry.getValue()
                    .parallelStream().sorted(Comparator.comparing(OrderAndCompareValue::getIndex))
                    .collect(Collectors.toList());

                int len = orderAndCompareValueList.size();
                if (len > 1) {
                    List<ErrorSequenceInfo> list = new ArrayList<>();
                    //len - 1 最后一个值不取
                    for (int i = 0, outerLen = len - 1; i < outerLen; i++) {
                        OrderAndCompareValue orderAndCompareValue = orderAndCompareValueList.get(i);
                        // i+1 取外层循环的后一个值
                        for (int j = i + 1; j < len; j++) {
                            OrderAndCompareValue innerOrderAndCompareValue = orderAndCompareValueList
                                .get(j);
                            //判断字段值顺序应与排序顺序是否一致
                            Integer firstIndex = orderAndCompareValue.getIndex();
                            Integer secondIndex = innerOrderAndCompareValue.getIndex();
                            Comparable<Object> firstValue = orderAndCompareValue
                                .getComparableValue();
                            Comparable<Object> secondValue = innerOrderAndCompareValue
                                .getComparableValue();
                            boolean conditionOne = firstIndex.compareTo(secondIndex)
                                == firstValue.compareTo(secondValue);
                            //判断是否包含前面的值，如果包含，则加上两值相等的条件
                            boolean conditionTwo = innerOrderAndCompareValue.isContainerPre() &&
                                firstValue.compareTo(secondValue) == 0;
                            //条件一和条件二都为false时才报错
                            if (!conditionOne && !conditionTwo) {
                                list.add(new ErrorSequenceInfo(key, orderAndCompareValue,
                                    innerOrderAndCompareValue));
                                break;
                            }
                        }
                    }
                    return list.stream();
                }
                return null;
            }).filter(Objects::nonNull).sorted(Comparator.comparing(ErrorSequenceInfo::getKey))
            .collect(Collectors.toList());
        if (errorSequenceInfoList.isEmpty()) {
            return true;
        } else {
            return createErrorMessage(context, errorSequenceInfoList);
        }
    }

    private boolean createErrorMessage(ConstraintValidatorContext context,
        List<ErrorSequenceInfo> errorSequenceInfoList) {
        context.disableDefaultConstraintViolation();
        String errorMessage = errorSequenceInfoList.parallelStream().map(item -> {
            OrderAndCompareValue one = item.getOne();
            OrderAndCompareValue another = item.getAnother();
            return MessageFormat
                .format("[{0}:字段[{1}]顺序为[{2}]值为[{3}],字段[{4}]顺序为[{5}]值为[{6}]]",
                    item.getKey().equals("") ? "默认组" : "组" + item.getKey(),
                    one.getFieldName(), one.getIndex(), one.getComparableValue(),
                    another.getFieldName(), another.getIndex(), another.getComparableValue()
                );
        }).collect(Collectors.joining(",", "{", "}"));

        context
            .buildConstraintViolationWithTemplate("字段值顺序错误：" + errorMessage)
            .addConstraintViolation();
        return false;
    }

    private void setValue2Map(Map<String, List<OrderAndCompareValue>> groupCompareMap,
        Field field,
        Object object) {
        if (field.isAnnotationPresent(Sequence.class)) {
            Sequence annotation = field.getAnnotation(Sequence.class);
            setStartValue2List(groupCompareMap, field, annotation, object);
        }
        if (field.isAnnotationPresent(Sequences.class)) {
            Sequences annotation = field.getAnnotation(Sequences.class);
            Sequence[] value = annotation.value();
            for (Sequence sequence : value) {
                setStartValue2List(groupCompareMap, field, sequence, object);
            }
        }
    }

    private void setStartValue2List(Map<String, List<OrderAndCompareValue>> groupCompareMap,
        Field field, Sequence annotation, Object object) {
        String group = annotation.group();
        List<OrderAndCompareValue> comparableList = groupCompareMap
            .computeIfAbsent(group, key -> new ArrayList<>());
        field.setAccessible(true);
        try {
            Object fieldValue = field.get(object);
            Optional.ofNullable(fieldValue).ifPresent(value ->
                comparableList.add(
                    new OrderAndCompareValue(field.getName(), annotation.value(),
                        annotation.equalPre(), ((Comparable<Object>) fieldValue))
                ));
        } catch (IllegalAccessException e) {
            log.error("插入对比值错误", e);
        }
    }

    @Data
    @AllArgsConstructor
    private class OrderAndCompareValue {

        private String fieldName;
        private int index;
        private boolean containerPre;
        private Comparable<Object> comparableValue;
    }

    @Data
    @AllArgsConstructor
    private class ErrorSequenceInfo {

        private String key;
        private OrderAndCompareValue one;
        private OrderAndCompareValue another;
    }

}
